using System;
using Gtk;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Collections.Generic;




using System.Text;
using System.Linq;

using ClientJIP;


public partial class MainWindow : Gtk.Window
{
	
	
	
	// Mutex
	Mutex mutexList = new Mutex();
	Mutex mutexText = new Mutex();
	Mutex mutexSocket = new Mutex();
	Mutex mutexInterface = new Mutex();
	
	// Model for TreeView
	Gtk.ListStore messageListLog;
	Gtk.ListStore messageListUser;
	Gtk.ListStore messageListPublic;
	
	
	public Dictionary<string, TreeView> PrivateTree;
	
	
	
	// Controleur
	private CtrlChat Ctrl;
	
	public MainWindow () : base(Gtk.WindowType.Toplevel)
	{
		Build ();
		
		AddTitle(treeviewLog, 2);
		AddTitle(treeviewPublic, 2);
		AddTitle(treeviewListUser, 1);
		
		// Create a temporary model
		messageListLog = new Gtk.ListStore (typeof (string), typeof (string));
		messageListUser = new Gtk.ListStore (typeof (string));
		messageListPublic = new Gtk.ListStore (typeof (string), typeof (string));
		
		// Set model to TreeView
		treeviewLog.Model = messageListLog;
		treeviewListUser.Model = messageListUser;
		treeviewPublic.Model = messageListPublic;
		
		
		treeviewPublic.ModifyBg(StateType.Normal, new Gdk.Color(255, 255, 255));
		
		// Auto-give focus for writting command
		entryCommand.IsFocus = true;
		
		//Initialize combobox default server
		comboboxentry_server.AppendText("192.168.0.187");
		comboboxentry_server.AppendText("localhost");
		comboboxentry_server.AppendText("127.0.0.1");
		
		comboboxentry_server.Active = 0;
		
		
		
		
		PrivateTree = new Dictionary<string, TreeView>();
		
		Ctrl = new CtrlChat(this);
	}
	
	/// <summary>
	/// Add the columns' title
	/// </summary>
	/// <param name="treeview">
	/// A <see cref="TreeView"/>
	/// </param>
	public void AddTitle(TreeView treeview, int column) {
		for(int i = 0; i < column; i++) {
			// Create a columns
			Gtk.TreeViewColumn newColumn = new Gtk.TreeViewColumn ();
	 
			// Add the columns to the TreeView
			treeview.AppendColumn (newColumn);
	 
			// Create the text cell
			Gtk.CellRendererText cell = new Gtk.CellRendererText ();
			newColumn.PackStart (cell, true);
			
			// Tell the Cell Renderers which items in the model to display
			newColumn.AddAttribute (cell, "text", i);
			
			if(i == 0) {
				newColumn.SetCellDataFunc (cell, new Gtk.TreeCellDataFunc (RenderCellHour));
			} else {
				newColumn.SetCellDataFunc (cell, new Gtk.TreeCellDataFunc (RenderCellText));	
			}
		}
	}
	
	public int GetNbPage() {
		
		return notebook_channel.NPages;
	}
	
	/// <summary>
	/// Set text formatting for pseudo and hour area
	/// </summary>
	/// <param name="column">
	/// A <see cref="Gtk.TreeViewColumn"/>
	/// </param>
	/// <param name="cell">
	/// A <see cref="Gtk.CellRenderer"/>
	/// </param>
	/// <param name="model">
	/// A <see cref="Gtk.TreeModel"/>
	/// </param>
	/// <param name="iter">
	/// A <see cref="Gtk.TreeIter"/>
	/// </param>
	public void RenderCellHour(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
	{
		String valueCell = (String) model.GetValue (iter, 0);
 		
		(cell as Gtk.CellRendererText).Background = "#EFEFEF";
		(cell as Gtk.CellRendererText).Foreground = "black";
 
		(cell as Gtk.CellRendererText).Text = valueCell;
	}
	
	/// <summary>
	/// Set text formatting for text area
	/// </summary>
	/// <param name="column">
	/// A <see cref="Gtk.TreeViewColumn"/>
	/// </param>
	/// <param name="cell">
	/// A <see cref="Gtk.CellRenderer"/>
	/// </param>
	/// <param name="model">
	/// A <see cref="Gtk.TreeModel"/>
	/// </param>
	/// <param name="iter">
	/// A <see cref="Gtk.TreeIter"/>
	/// </param>
	public void RenderCellText(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
	{
		String valueCell = (String) model.GetValue (iter, 1);
 		String valueCell1 = (String) model.GetValue (iter, 0);
	
		(cell as Gtk.CellRendererText).Background = "white";
		if (valueCell1.Length > 11) {
			if(valueCell1.Substring(11).ToLower() == Ctrl.GetNick().ToLower()) {
				(cell as Gtk.CellRendererText).Foreground = "#60637C";
			} else {
				(cell as Gtk.CellRendererText).Foreground = "black";
			}
		} else {
			(cell as Gtk.CellRendererText).Foreground = "darkblue";
		}
 
		(cell as Gtk.CellRendererText).Text = valueCell;
	}
	
	/// <summary>
	/// Clear the public treeview
	/// </summary>
	public void ResetPublic() {
		Gtk.Application.Invoke(delegate {
			// Create a model
			messageListPublic = new Gtk.ListStore (typeof (string), typeof (string));
			
			//Set model to interface
			treeviewPublic.Model = messageListPublic;
		});
	}

	
	/// <summary>
	/// Write message on log screen
	/// </summary>
	/// <param name="message">
	/// A <see cref="String"/>
	/// </param>
	public void WriteTextLog(String message)
	{
		mutexText.WaitOne();
		Gtk.Application.Invoke(delegate {
		
			TreeIter iter = messageListLog.AppendValues(DateTime.Now.ToLongTimeString(),message);
			
			//auto-scroll
			treeviewLog.ScrollToCell(messageListLog.GetPath(iter), null, false, 0, 0);
		});
		
		mutexText.ReleaseMutex();
	}
	
	/// <summary>
	/// Write message on selected public screen
	/// </summary>
	/// <param name="message">
	/// A <see cref="String"/>
	/// </param>
	public void WriteTextPrivate(string message, string pseudo_write, string pseudoPage) {
		mutexText.WaitOne();
		
		try {
			Gtk.Application.Invoke(delegate {
				
				TreeView tree = PrivateTree[pseudoPage.Trim()];
				ListStore list = (ListStore)tree.Model;
				
				TreeIter iter = list.AppendValues("[" + DateTime.Now.ToLongTimeString() + "] " + pseudo_write,message);
				
				//auto-scroll
				tree.ScrollToCell(list.GetPath(iter), null, false, 0, 0);
			});
		}
		catch {
			
		}
		
		mutexText.ReleaseMutex();
	}
	
	/// <summary>
	/// Write message
	/// </summary>
	/// <param name="message">
	/// A <see cref="String"/>
	/// </param>
	/// <param name="pseudo_write">
	/// A <see cref="String"/>
	/// </param>
	public void WriteTextPublic(String message, String pseudo_write) {
		mutexText.WaitOne();
		
		Gtk.Application.Invoke(delegate {
		
			TreeIter iter = messageListPublic.AppendValues("[" + DateTime.Now.ToLongTimeString() + "] " + pseudo_write,message);
			
			//auto-scroll
			treeviewPublic.ScrollToCell(messageListPublic.GetPath(iter), null, false, 0, 0);
		});
		
		
		mutexText.ReleaseMutex();
	}

	/// <summary>
	/// Close application
	/// </summary>
	protected virtual void ExitApplication()
	{
		Ctrl.writeToServeur("/QUIT");
		Application.Quit ();
	}
	
	/// <summary>
	/// Set buttons to correct connect status
	/// </summary>
	/// <param name="status">
	/// A <see cref="Boolean"/>
	/// </param>
	public void SetInterfaceConnected(Boolean status) {
		mutexInterface.WaitOne();
		
		
		Gtk.Application.Invoke(delegate {
			//Change control access
			
			comboboxentry_server.Sensitive = !status;
			ConnecterAction.Sensitive = !status;
			ConnexionAction.Sensitive = !status;
			DeconnecterAction.Sensitive = status;
			DisconnectAction.Sensitive = status;
		});
		
		
		mutexInterface.ReleaseMutex();
	}
	
	/// <summary>
	/// Set button to correct status . 0. Afk, 1. Nfk, Other. Offline
	/// </summary>
	/// <param name="status">
	/// A <see cref="System.Int32"/>
	/// </param>
	public void SetInterfaceModeTalk(int status)
	{
		mutexInterface.WaitOne();
		
		
		Gtk.Application.Invoke(delegate {
			switch (status) {
			case 0:
				EnLigneAction.Sensitive = true;
				AbsentAction.Sensitive = false;
				break;
			case 1:
				EnLigneAction.Sensitive = false;
				AbsentAction.Sensitive = true;
				break;
			default:
				EnLigneAction.Sensitive = false;
				AbsentAction.Sensitive = false;
				break;
			}
		});
		
		mutexInterface.ReleaseMutex();
	}
	
	/// <summary>
	/// Set button to correct send file status. true. Available, false. Unavalaible
	/// </summary>
	/// <param name="status">
	/// A <see cref="Boolean"/>
	/// </param>
	public void SetInterfaceConv(Boolean status) {
		
		mutexInterface.WaitOne();
		
		
		Gtk.Application.Invoke(delegate {
			//Change control access
			SendFileAction.Sensitive = status;
			ArreteConvAction.Sensitive = status;
		});
		
		mutexInterface.ReleaseMutex();
		
	}
	
	/// <summary>
	/// Disconnect from the server
	/// </summary>
	protected void DisconnectServer() {
		mutexSocket.WaitOne();
			
		Gtk.Application.Invoke(delegate {
	
			SetInterfaceModeTalk(2);
			SetInterfaceConnected(false);
		});
			
		Ctrl.quitChat();

		
		mutexSocket.ReleaseMutex();
	}	
	
	
	/// <summary>
	/// Analyse the server response/message
	/// </summary>
	/// <param name="command">
	/// A <see cref="String"/>
	/// </param>
	protected void AnalyseResponse(String command) {

	}
	
	/// <summary>
	/// Execute connected event steps for client
	/// </summary>
	public void ConnectToChat() {
		
		Gtk.Application.Invoke(delegate {
			SetInterfaceModeTalk(1);
			cadrePublic.Visible = true;
			
			//Thread.Sleep(10);
			notebook_channel.Page = 1;
		});
	}
	
	/// <summary>
	/// Execute quit event steps for client
	/// </summary>
	public void QuitChatSession() {
		Gtk.Application.Invoke(delegate {
			cadrePublic.Visible = false;
		});
	}
	
	/// <summary>
	/// Execute close conversation event steps for client
	/// </summary>
	/// <param name="pseudoDelete">
	/// A <see cref="System.String"/>
	/// </param>
	public void DeletePageTalk(int index) {
		Gtk.Application.Invoke(delegate {
			notebook_channel.RemovePage(index);
		});
		
		
		PrivateTree.Remove(Ctrl.ConnectedAtPage(index));
	}
	
	
	/// <summary>
	/// Execute afk change event steps for client
	/// </summary>
	public void SwitchToAfk() {
		mutexInterface.WaitOne();
		
		
		Gtk.Application.Invoke(delegate {
			SetInterfaceModeTalk(0);
			cadrePublic.Visible = false;
		});
		
		mutexInterface.ReleaseMutex();
	}
	
	public void SwitchToNfk() {
		mutexInterface.WaitOne();
		
		Gtk.Application.Invoke(delegate {
			cadrePublic.Visible = true;
			notebook_channel.Page = 1;
			SetInterfaceModeTalk(1);
			
		});
		
		mutexInterface.ReleaseMutex();
	}
	
	
	public void ChangeNick(String oldPseudo, String newPseudo) {
		if(PrivateTree.Keys.Contains(oldPseudo)) {
			PrivateTree.Add(newPseudo, PrivateTree[oldPseudo]);
			PrivateTree.Remove(oldPseudo);
		}
		
		Ctrl.WriteListUser();
	}
	
	
	
	public void WriteListUser(Dictionary<string, int> connected) {
		mutexInterface.WaitOne();;
		
		
		
		Gtk.Application.Invoke(delegate{
			// Create a model
			messageListUser = new Gtk.ListStore (typeof (string));
			
			
			
			mutexList.WaitOne();
			
			
			// Sort
			var tmp = from entry in connected orderby entry.Key ascending select entry.Key;
			    foreach(String user in tmp) {
				//Thread.Sleep(5);
					messageListUser.AppendValues(user);
				}
			mutexList.ReleaseMutex();
			
			
			//Set model to interface
			treeviewListUser.Model = messageListUser;
		});
		
		mutexInterface.ReleaseMutex();
	}
	
	
	void CloseTalk(int index) {
		mutexText.WaitOne();
		DeletePageTalk(index);
		mutexText.ReleaseMutex();
	}
	
	void unknown() {
        WriteTextPublic("Commande inconnue.", "System");
	}
	
		
	public void AcceptTalk(string pseudoTalker) {
		Gtk.Application.Invoke(delegate{
			Label titre = new Label(pseudoTalker);
			
			TreeView tree = new TreeView();
			PrivateTree.Add(pseudoTalker, tree);
			
			
			tree.Visible = true;
			tree.HeadersVisible = false;
			AddTitle(tree, 2);
			
			ScrolledWindow scroll = new ScrolledWindow();
			scroll.Visible = true;
			scroll.Add(tree);
			
			ListStore model = new ListStore(typeof (string), typeof (string));
			
			tree.Model = model;
			
			notebook_channel.AppendPage(scroll, titre);
		});
	}
	
	/*EVENTS*/
	
	/// <summary>
	/// Send user command to server
	/// </summary>
	/// <param name="sender">
	/// A <see cref="System.Object"/>
	/// </param>
	/// <param name="e">
	/// A <see cref="System.EventArgs"/>
	/// </param>
	protected virtual void OnClickSend (object sender, System.EventArgs e)
	{
		string message = entryCommand.Text.Trim();
		
		if(message == String.Empty || !Ctrl.SendToChat(message, notebook_channel.Page))
			return;
		
		else {
			entryCommand.Text = "";
			entryCommand.IsFocus = true;
		}
	}
	
	/// <summary>
	/// Establish connection to the server
	/// </summary>
	/// <param name="sender">
	/// A <see cref="System.Object"/>
	/// </param>
	/// <param name="e">
	/// A <see cref="System.EventArgs"/>
	/// </param>
	protected virtual void OnConnect (object sender, System.EventArgs e) {
		Ctrl.ConnectedToServeur(comboboxentry_server.ActiveText);
	}
	
	/// <summary>
	/// Disconnect user from server
	/// </summary>
	/// <param name="sender">
	/// A <see cref="System.Object"/>
	/// </param>
	/// <param name="e">
	/// A <see cref="System.EventArgs"/>
	/// </param>
	protected virtual void OnDisconnect (object sender, System.EventArgs e) {
		DisconnectServer();
	}
	
	/// <summary>
	/// Quit application if user choose exit in menu
	/// </summary>
	/// <param name="sender">
	/// A <see cref="System.Object"/>
	/// </param>
	/// <param name="e">
	/// A <see cref="System.EventArgs"/>
	/// </param>
	protected virtual void OnQuitEvent (object sender, System.EventArgs e)
	{
		Ctrl.quitChat();
		ExitApplication();
	}
	
	/// <summary>
	/// Quit application if user close the window
	/// </summary>
	/// <param name="sender">
	/// A <see cref="System.Object"/>
	/// </param>
	/// <param name="a">
	/// A <see cref="DeleteEventArgs"/>
	/// </param>
	protected void OnDeleteEvent (object sender, DeleteEventArgs a) {
		Ctrl.quitChat();
		ExitApplication();
		a.RetVal = true;
	}
	
	[GLib.ConnectBefore]
	protected virtual void OnTreeviewListUserButtonPressEvent (object o, Gtk.ButtonPressEventArgs args) { 
		try{
			if ( args.Event.Type == Gdk.EventType.TwoButtonPress) {
				TreeView tree = (TreeView ) o;
				TreePath path;
				TreeViewColumn column;
				tree.GetCursor(out path, out column);
				
				TreeIter iter;
				tree.Model.GetIter(out iter, path);
				
				
				string pseudo_click = tree.Model.GetValue(iter, 0).ToString();
				
				Ctrl.SendAskTalk(pseudo_click);
		    }
		}
		catch {}
	}
	
	protected virtual void OnNfk (object sender, System.EventArgs e)
	{
		EnLigneAction.Sensitive = false;
		Ctrl.writeToServeur("/NFK");
	}
	
	protected virtual void OnAfk (object sender, System.EventArgs e)
	{
		AbsentAction.Sensitive = false;
		Ctrl.writeToServeur("/AFK");
	}
	
	protected virtual void OnSendFile (object sender, System.EventArgs e)
	{
		String tmpPseudo = Ctrl.ConnectedAtPage(notebook_channel.Page);
		
	    Gtk.FileChooserDialog fc = new Gtk.FileChooserDialog("Choisissez votre fichier", this, FileChooserAction.Open, "Annuler",ResponseType.Cancel, "Choisir",ResponseType.Accept);
		if (fc.Run() == (int)ResponseType.Accept) 
		{
			Console.WriteLine("Fichier choisi : " + fc.Filename + "\n");
			
			Ctrl.FilesToSendClient(System.IO.Path.GetFileName(fc.Filename),fc.Filename,tmpPseudo,(new FileInfo(fc.Filename)).Length);
		}
		
		fc.Destroy();
	}
	
	protected virtual void OnChangePage (object o, Gtk.SwitchPageArgs args)
	{
		if(notebook_channel.Page > 1)
		{
			SetInterfaceConv(true);
		}
		else
		{
			SetInterfaceConv(false);
		}
	}
	
	protected virtual void OnCloseConv (object sender, System.EventArgs e)
	{
		DeletePageTalk(notebook_channel.Page);
		Ctrl.CloseConv(notebook_channel.Page);
	}
	
	[GLib.ConnectBefore]
	protected virtual void OnPressTreeView (object o, Gtk.ButtonPressEventArgs args)
	{
		// check if the right button was pushed
	    if((int)args.Event.Button == 3)
	    {       
	        Gtk.Menu jBox = new Gtk.Menu();
	        Gtk.MenuItem MenuItemCopy = new MenuItem("Copier");
			MenuItemCopy.Activated += new EventHandler(MenuItemCopy_Click);
	        jBox.Add(MenuItemCopy);               
	        jBox.ShowAll();
	        jBox.Popup();
	    }
	}
	
	private void MenuItemCopy_Click(object sender, System.EventArgs e)
	{
		TreeView tree = treeviewLog;
		TreePath path;
		TreeViewColumn column;
		tree.GetCursor(out path, out column);
		TreeIter iter;
		if(path != null) {
			tree.Model.GetIter(out iter, path);
			Clipboard clippy = GetClipboard(Gdk.Atom.Intern("CLIPBOARD",false));
			clippy.Text = treeviewLog.Model.GetValue(iter, 0).ToString() + " : " + treeviewLog.Model.GetValue(iter, 1).ToString();
		}
	}
	
	public void WindowsAskTalk(string pseudo)
	{
		Gtk.Application.Invoke(delegate {
		Gtk.MessageDialog md = new Gtk.MessageDialog (this, Gtk.DialogFlags.Modal, Gtk.MessageType.Question, 
			                                      Gtk.ButtonsType.YesNo, pseudo + " vous a envoyé une demande de conversation, accepter?");
			
		Gtk.ResponseType result = (Gtk.ResponseType)md.Run ();
		
		if (result == Gtk.ResponseType.Yes) {
			Ctrl.writeToServeur("/ACCEPT " + pseudo);
			Ctrl.AcceptTalk(pseudo);
				Ctrl.AddConnectionPage(notebook_channel.NPages, pseudo);
		} else
			Ctrl.writeToServeur("/REJECT " + pseudo);
			
		md.Destroy();
		});
	}
	
	public void WindowsAskFile (String pseudo)
	{
		
		Gtk.Application.Invoke(delegate {
		Gtk.MessageDialog md = new Gtk.MessageDialog (this, Gtk.DialogFlags.Modal, Gtk.MessageType.Question, 
			                                      Gtk.ButtonsType.YesNo, pseudo + " souhaite vous envoyer un fichier, accepter?");
			
		Gtk.ResponseType result = (Gtk.ResponseType)md.Run ();
		
		if (result == Gtk.ResponseType.Yes)
			Ctrl.AcceptFile(pseudo);
		else
			Ctrl.RefuseFile(pseudo);
			
		md.Destroy();
		});
	}
}

